#!/usr/bin/python -tt

import urllib
import json
import sys
import argparse

apiurl = 'https://www.cloudflare.com/api_json.html'
## options required for requests
apiparams = {}
# need for all requests
apiparams['basic'] = {'tkn' : 'asdfjahsgdfkashgdkfasdk9d38e44fc857fd', 'email' : 'cloudflarelogin@example.com', 'act' : ''}

# 3.1 - "stats" - Retrieve domain statistics for a given time frame
'''
"z"         The zone (domain) that statistics are being retrieved from
"interval"
            The time interval for the statistics denoted by these values: 
            For these values, the latest data is from one day ago
            20 = Past 30 days
            30 = Past 7 days
            40 = Past day

            The values are for Pro accounts
            100 = 24 hours ago
            110 = 12 hours ago
            120 = 6 hours ago
'''
apiparams['stats'] = {'act' : 'stats', 'z': '', 'interval' : 30}

# 3.2 - "zone_load_multi" - Retrieve the list of domains
apiparams['zone_load_multi'] = {'act' : 'zone_load_multi'}

# 3.3 - "rec_load_all" - Retrieve DNS Records of a given domain
# "z"       The domain that records are being retrieved from
apiparams['zone_load_all'] = {'act' : 'zone_load_all', 'z' : ''}

# 3.4 - "zone_check" - Checks for active zones and returns their corresponding zids
# "zones"   List of zones separated by comma
apiparams['zone_check'] = {'act' : 'zone_check', 'zones' : []}

# 3.5 - "zone_ips" - Pull recent IPs visiting site
# "z"       The target domain
# "hours"   Past number of hours to query. Default is 24, maximum is 48.
# "class"   Optional. Restrict the result set to a given class as given by:
#           "r" -- regular
#           "s" -- crawler
#           "t" -- threat
# "geo"     Optional. Set to 1 to add longitude and latitude information to response
apiparams['zone_ips'] = {'act' : 'zone_ips', 'z' : '', 'hours' : '', 'class' : '', 'geo' : 0}

# 3.6 - "ip_lkup" - Check threat score for a given IP
# "ip"      The target IP
apiparams['ip_lkup'] = {'act' : 'ip_lkup', 'ip' : '127.0.0.1'}

# 3.7 - "zone_settings" - List all current setting values
apiparams['zone_settings'] = {'act' : 'zone_settings', 'z' : ''}

# "example_func" - for debug requesting API unknown function
apiparams['wrong_api_func_for_testing'] = {'act' : 'wrong_api_func_for_testing', 'z' : ''}

# Modify requests

def request(command):
    apiurl = 'https://www.cloudflare.com/api_json.html'
    params = {'tkn': 'asjhfdlkashdflashdlfds99d38e44fc857fd', 'email': 'cloudflarelogin@example.com'}

    if command == 'stats':
        params['act'] = 'stats'
        params['interval'] = 120
        #params['z'] = ''
        print params

    if command == 'zone_load_multi':
        params['act'] = 'zone_load_multi'
        print params

    requestparams = urllib.urlencode(params)
    fh = urllib.urlopen(apiurl, requestparams)
    return fh.read()

def request2(command):
    #params = {'tkn': 'ashfdlasjhfdjahsdjhalsdjfs8e44fc857fd', 'email': 'cloudflarelogin@example.com'}

    this_function_name = sys._getframe().f_code.co_name

    # test that user supplyed command is known 
    commandsavailable = apiparams.keys()
    commandsavailable.sort()
    if not (command in commandsavailable):
        print 'Unknown command: {0} in {1}'.format(command, this_function_name)
        return 0
    
    # current request options
    reqoptions = {}
    reqoptions.update(apiparams['basic'])
    reqoptions.update(apiparams[command])
    if len(reqoptions):
        print '**************************************************************'
        print "REQ: {0} OPTIONS: {1}".format(command, reqoptions)
    else:
        print "REQ: {0} OPTIONS: {1}".format(command, 'EMPTY OPTIONS LIST')

    requestparams = urllib.urlencode(reqoptions)
    fh = urllib.urlopen(apiurl, requestparams)
    # this is a string !!!!!
    reply = fh.read()
    replyjson = json.loads(reply)
    
    # print reply['msg']
    if replyjson['result'] == 'error':
        print "    >>>API returns ERROR in {0} request>>>".format(command)
        print "    >>>API returns ERROR in {0} debug  >>> {1}".format(command, replyjson)
        print "    >>>replyjson: {0}".format(replyjson)
        # return FALSE
        return replyjson
    return replyjson

def request3(command):
    '''Send request to API URL and Return CloudflareReply'''
    ''' For test returns string'''
    #print "REQUEST3"
    # current request options
    reqoptions = {}
    reqoptions.update(apiparams['basic'])
    reqoptions.update(apiparams[command])

    requestparams = urllib.urlencode(reqoptions)
    #print requestparams
    fh = urllib.urlopen(apiurl, requestparams)
    # this is a string !!!!!
    reply = fh.read()
    #print "reply {}".format(reply)
    #replyjson = json.loads(reply)

    return reply

class CloudFlareRequest:
    '''
    Response example
    {
     "request": {
    "act": "zone_load_multi",
    "a": "zone_load_multi",
    "email": "sample@example.com",
    "tkn": "8afbe6dea02407989af4dd4c97bb6e25"
    },
     "response": {
    ...
    }
    ...
    }
    '''
    pass

class CloudFlareReply:
    def __init__(self, jsonstring):
        '''convert string representation of JSON reply to Python structures'''
        self.raw = json.loads(jsonstring)
        self.request = {}
        self.req()
        self.response = {}
        self.resp()
        '''success or error'''
        self.result = self._result()
        '''
        If there are errors, the JSON response will include a detailed message in the "msg" field,
        and sometimes will include one of the following error codes:

        "E_UNAUTH" -- Authentication could not be completed
        "E_INVLDINPUT" -- Some other input was not valid
        "E_MAXAPI" -- You have exceeded your allowed number of API calls.
        '''
        self.err_code = self.err_code()
        ## None or string description of the error
        self.msg = self.msg()
    def req(self):
        '''Check request section in reply'''
        if 'request' in self.sections():
            self.request.update(self.raw['request'])
            return self.request
        else:
            return None
    def resp(self):
        '''Check response section in reply'''
        '''Returns dict'''
        if 'response' in self.sections():
            self.response.update(self.raw['response'])
            return self.response
        else:
            return None
    def _result(self):
        '''Check result section existance int self.raw assign self.result and print it'''
        if 'result' in self.sections():
            self.result = self.raw['result']
            return self.result
        else:
            return None
    def err_code(self):
        '''Check err_code in reply'''
        if 'err_code' in self.sections():
            self.err_code = self.raw['err_code']
            return self.err_code
        else:
            return None
    def msg(self):
        '''Check msg in reply'''
        if 'msg' in self.sections():
            self.msg = self.raw['msg']
            return self.msg
        else:
            return None
    '''Returns section names which exists in Cloudflare API response'''
    def sections(self):
        return self.raw.keys()
    def __str__(self):
        _str = '''
<CloudflareReply>
 req         = {}
 response    = {}
 result      = {}
 err_code    = {}
 msg         = {}
</CloudflareReply>
            '''.format(self.request, self.response, self.result, self.err_code, self.msg)
        return _str


def main():

    '''
    print request('stats')
    reply = request('zone_load_multi')

    replyjson = json.loads(reply)
    print replyjson['request']['act'], replyjson['result']
    print replyjson['msg']
    apiparams['zone_check']['zones'].append('unlimite.net')
    apiparams['zone_check']['zones'].append('test.com')

    # print requests params
    for k, v in sorted(apiparams.iteritems()):
        print k, v

    '''
    apirequests = apiparams.keys()
    apirequests.sort()

    ## all in apiparams.keys() except 'basic' is valid Cloudflare API functions
    ## construct list of valid commands
    commands  = []
    commands.extend(apirequests)
    commands.remove('basic')
    commands.sort()
    ## DEBUG
    #print commands
    #print apirequests
    #return 1

    parser = argparse.ArgumentParser(
        description='Cloudflare.com API wrapper',
        epilog='API details at https://www.cloudflare.com/docs/client-api.html')
    parser.add_argument("command", choices=commands, nargs='?', metavar='command', help='Cloudflare API request: (%(choices)s)')
    #parser.add_argument("-l", "--list", action="store_true", help='List all available Cloudflare requests')
    parser.add_argument("-t", "--test", action="store_true", help='Test all available Cloudflare requests')
    parser.add_argument("--tkn", required=True, help='CloudFlare API key (required)')
    parser.add_argument("--email", required=True, help='CloudFlare account email address (required)')
    args = parser.parse_args()

    ## Check required args
    ## works
    '''
    if args.tkn is None:
        print "args.tkn is None"
    else:
        print args.tkn
    print args.email

    ## Check required args
    ## works
    if None in [args.tkn, args.email]:
        print "email or tkn is None"
    '''

    '''
    # test that user supplyed command is known 
    command = 'test'
    if not (command in commandsavailable):
        print 'Unknown command: {0}'.format(command)

    # current request options
    command = 'stats'
    reqoptions = {}
    reqoptions.update(apiparams['basic'])
    reqoptions.update(apiparams[command])
    print  "REQ: {0} OPTIONS: {1}".format(command, reqoptions)
    request2('test')
    '''
    '''
    # reply is dict
    reply = json.loads(request2('stats'))
    
    if reply['err_code'] == 'E_UNAUTH':
        print "Authentification failed: {0}".format(reply['msg'])
    elif reply['err_code'] == "E_INVLDINPUT":
        print "Some other input was not valid: {0}".format(reply['msg'])
    elif reply['err_code'] == "E_MAXAPI":
        print "You have exceeded your allowed number of API calls: {0}".format(reply['msg'])
    #print reply[]
    '''

    if args.test:
        print "Test ALL available Cloudflare API requests"
        #return 1
        for command in commands:
            if command != 'basic':
                #reply = request2(command)
                replyobj = CloudFlareReply(request3(command))
                # for debug
                print ">>> Call {}".format(command)
                print replyobj
                del replyobj
                '''
                #if reply['result'] == 'error':
                if not reply:
                    #print "Unknown error"
                    # return FALSE
                    return 0
                
                
                ## HERE WE MUST CHECK existense of all Cloudfare API JSON response parameters???
                if 'response' not in reply.keys():
                    reply.update({'response' : '****request2()****'})
                if 'err_code' not in reply.keys():
                    reply.update({'err_code' : '****request2()****'})
                '''

                '''
                # if Cloudfare API returns 'success' than we gets valid response 
                if reply['result'] == 'success':
                    print "    REP: {0}".format(reply['response'])
                    continue

                if reply['err_code'] == 'E_UNAUTH':
                    print "Authentification failed: {0}".format(reply['msg'])
                elif reply['err_code'] == "E_INVLDINPUT":
                    print "Some other input was not valid: {0}".format(reply['msg'])
                elif reply['err_code'] == "E_MAXAPI":
                    print "You have exceeded your allowed number of API calls: {0}".format(reply['msg'])
                '''
        return

    #print "command is {}".format(args.command)
    
    ## if 'command' not supplyed by user args.command is None
    #if "{}".format(args.command)=='None':
    if not args.command:
        parser.print_help()
    else:
        reply = request2(args.command)
        print reply

        replyobj = CloudFlareReply(request3(args.command))
        #print replyobj.sections()
        #print replyobj.result
        #print replyobj.msg
        #print replyobj.err_code
        print replyobj
        del replyobj

if __name__ == '__main__':
    main()

